package mua.core.interpreter;

import mua.core.operation.MuaOperation;
import mua.core.operation.basis.Operator;
import mua.core.operation.basis.Thing;
import mua.core.value.FunctionBody;
import mua.core.value.MuaValue;
import mua.core.value.Number;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Stack;

import static mua.core.interpreter.Util.isNumber;
import static mua.core.interpreter.Util.myTrim;

/**
 * 该类用于计算仅包含":"（即thing，取值）, "+", "-", "*", "/", "%"的中缀表达式
 * e.g. (2*(-2+6)/4), (5%3-3*3/(5+4)), (:a+1*2)
 * @date 2020年10月30日
 * @author 彭子帆
 */
public class ArithmeticExpression {
    public static Number parseExpression(String expression, FunctionBody functionBody){
        Stack<String> operationStack = new Stack<>();
        Stack<MuaValue> valueStack = new Stack<>();
        //存储栈操作符
        Stack<String> operationsStack = new Stack<>();
        //存储后缀表达式
        List<String> suffix = new ArrayList<>();
        //存储前缀表达式
        StringBuilder prefix = new StringBuilder();
        //将String转List
        List<String> expressionList = stringToList(expression, functionBody);
        //从左向右扫描
        for(String item : expressionList){
            /*
            如果扫描到的字符是一个操作符，分三种情况：
            （1）如果堆栈是空的，直接入栈
            （2）如果该操作符的优先级优先级大于栈顶操作符，直接入栈
            （3）如果该操作符的优先级小于等于栈顶操作符，将栈顶的操作符弹出并入队, 直到该操作符的优先级大于栈顶操作符。
             */
            if(isOperator(item)){
                //为空
                if(operationsStack.empty()){
                    operationsStack.push(item);
                }
                //优先级大于栈顶操作符，注意："("视为优先级为-1，任何操作符可以入队
                else if(priority(item)>priority(operationsStack.peek())){
                    operationsStack.push(item);
                }
                //优先级小于等于栈顶操作符，将栈顶的操作符弹出并入队, 直到该操作符的优先级大于栈顶操作符。
                else {
                    while (!operationsStack.empty()){
                        //遇到大于当前操作符优先级或者遇到左括号时，退出
                        if("(".equals(operationsStack.peek())||priority(item)>priority(operationsStack.peek())){
                            break;
                        }
                        //否则加到List中
                        else{
                            suffix.add(operationsStack.pop());
                        }
                    }
                    operationsStack.push(item);
                }
            }
            //如果扫描到的字符是操作数，直接入队
            else if(isNumber(item)){
                suffix.add(item);
            }
            //如果遇到的操作符是左括号"("，就直接将该操作符输出到堆栈当中。
            else if("(".equals(item)){
                operationsStack.push(item);
            }
            //如果扫描到的操作符是右括号")"，将堆栈中的操作符弹出入队，直到遇见左括号"("。
            else if(")".equals(item)){
                while (!operationsStack.isEmpty()){
                    //遇到左括号，break
                    if("(".equals(operationsStack.peek())){
                        operationsStack.pop();
                        break;
                    }
                    //将括号间的操作都入队
                    else {
                        suffix.add(operationsStack.pop());
                    }
                }
            }
        }
        //扫描完毕，但是堆栈中仍然存在操作符的时候，将堆栈中的操作符入队
        while (!operationsStack.isEmpty()){
            suffix.add(operationsStack.pop());
        }
        //将suffix反转且需要处理除法，减法和取余（调换a-b的顺序）
        Collections.reverse(suffix);
        for(String item:suffix){
            switch (item){
                case "+":
                    prefix.append("add ");
                    break;
                case "-":
                    prefix.append("sub ");
                    break;
                case "*":
                    prefix.append("mul ");
                    break;
                case "/":
                    prefix.append("div ");
                    break;
                case "%":
                    prefix.append("mod ");
                    break;
                default:
                    prefix.append(item).append(" ");
            }
        }
        parseLine(operationStack,valueStack,prefix.toString(), functionBody);
        return (Number) valueStack.pop();
    }

    /**
     * 判断字符串是否为操作符
     * @param operator 操作符
     * @return 返回是否为操作符
     */
    public static boolean isOperator(String operator){
        return operator.equals("+")||operator.equals("-")||
                operator.equals("*")||operator.equals("/")||operator.equals("%");
    }

    /**
     * 获取操作符的优先级
     * @param operator 操作符
     * @return 返回优先级顺序，加减为0，乘除取余为1
     * 注：将")"视为优先级-1，因为遇到"("也可直接入队
     */
    public static int priority(String operator){
        if(operator.equals("*")||operator.equals("/")||operator.equals("%")){
            return 1;
        }else if(operator.equals("+")||operator.equals("-")){
            return 0;
        }
        return -1;
    }

    /**
     * 用于将String类型的表达式转换为List类型存储的表达式，取值要取出
     * @param expression String类型的表达式如"1+-2*3-:a"
     * @return e.g. 返回List{1,+,(,0,-,2,),*,3,-,(a的值)}
     */
    private static List<String> stringToList(String expression, FunctionBody functionBody){
        int i = 0;
        char ch1, ch2;
        //负数如下处理，e.g.
        //-:a则变成{'(','0','-','a的值',')'}，即(0-a)
        //-1则变成{'(','0','-','1',')'}，即(0-1)
        //用于判断上一个字符是否为操作符，从而判断是负数还是表达式"a-b"
        boolean last = true;
        //用于判断当前操作数是否是负数
        boolean isNeg = false;

        String withoutSpace = expression.replace(" ","");
        List<String> result = new ArrayList<>();
        StringBuilder part = new StringBuilder();

        do{
            ch1 = withoutSpace.charAt(i);
            //为取值操作":"，将后面直到下一个操作符或空格为止的字符都加入到临时String里
            if(ch1==':'){
                while(i+1<withoutSpace.length()){
                    i++;
                    ch2 = withoutSpace.charAt(i);
                    if(isOperator(ch2+"")||ch2==' '){
                        break;
                    }
                    part.append(ch2);
                }
                //负数
                result.add(Thing.run(new MuaValue(part.toString()),functionBody).toString());
                if(isNeg){
                    result.add(")");
                    isNeg = false;
                }
                //不是操作符
                last = false;
                part = new StringBuilder();
            }
            //数字，需要判断是否数字为多位
            else if((ch1<='9'&&ch1>='0')||ch1=='.'){
                while (i < withoutSpace.length()){
                    ch2 = withoutSpace.charAt(i);
                    if((ch2>='0'&&ch2<='9')||ch2=='.') {
                        part.append(withoutSpace.charAt(i));
                        i++;
                    }
                    else break;
                }
                //负数
                result.add(part.toString());
                if(isNeg){
                    result.add(")");
                    isNeg = false;
                }
                //不是操作符
                last = false;
                part = new StringBuilder();
            }
            //操作符
            else{
                if(ch1=='-'&&last){
                    result.add("(");
                    result.add("0");
                    result.add("-");
                    isNeg = true;
                    last = false;
                }else{
                    result.add(ch1+"");
                    last = true;
                }
                i++;
            }
        }while (i<withoutSpace.length());
        return result;
    }

    /** 单独处理加减乘除的前缀表达式处理，从右向左扫描
     * @param line 输入的一行
     */
    public static void parseLine(Stack<String> operationStack, Stack<MuaValue> valueStack,
                                 String line, FunctionBody functionBody){
        valueStack.clear();
        String[] part = split(line);
        while(part[1]!=null&&!part[1].isEmpty()){
            parseOne(operationStack,valueStack,part[1],functionBody);
            part = split(part[0]);
        }
        parseOne(operationStack,valueStack,part[0],functionBody);
    }

    /**
     * 每次输入一个value或operation都运行一次
     * @param s 最后一部分字符串
     */
    private static void parseOne(Stack<String> operationStack, Stack<MuaValue> valueStack,
                                 String s, FunctionBody functionBody) {
        if(MuaOperation.contains(s, functionBody)){
            operationStack.push(s);
            //尝试进行解析，此处传参是引用传参而非值传递
            run(operationStack, valueStack);
        } else {
            valueStack.push(new Number(s));
        }
    }

    /**
     * 输入个字符串，以最后一个空格分割两部分。
     * 若无空格仅返回第一部分，第二部分设为null。
     * @param str String
     * @return 返回第一个空格前后两部分
     */
    private static String[] split(String str){
        str = myTrim(str);
        int index = str.lastIndexOf(' ');
        if(index==-1){
            return new String[]{str,null};
        }else{
            return new String[]{str.substring(0,index),str.substring(index+1)};
        }
    }

    public static boolean run(Stack<String> operationStack, Stack<MuaValue> valueStack) {
        if (!operationStack.empty()){
            String opt = operationStack.pop();
            MuaValue a, b;
            //判断是否为基本操作
            switch (opt){
                case "add":
                    if(valueStack.empty())return false;
                    a = valueStack.pop();
                    if(valueStack.empty()){
                        valueStack.push(a);
                        return false;
                    }
                    b = valueStack.pop();
                    valueStack.push(Operator.add(a, b));
                    break;
                case "sub":
                    if(valueStack.empty())return false;
                    a = valueStack.pop();
                    if(valueStack.empty()){
                        valueStack.push(a);
                        return false;
                    }
                    b = valueStack.pop();
                    valueStack.push(Operator.sub(b, a));
                    break;
                case "mul":
                    if(valueStack.empty())return false;
                    a = valueStack.pop();
                    if(valueStack.empty()){
                        valueStack.push(a);
                        return false;
                    }
                    b = valueStack.pop();
                    valueStack.push(Operator.mul(a, b));
                    break;
                case "div":
                    if(valueStack.empty())return false;
                    a = valueStack.pop();
                    if(valueStack.empty()){
                        valueStack.push(a);
                        return false;
                    }
                    b = valueStack.pop();
                    valueStack.push(Operator.div(b, a));
                    break;
                case "mod":
                    if(valueStack.empty())return false;
                    a = valueStack.pop();
                    if(valueStack.empty()){
                        valueStack.push(a);
                        return false;
                    }
                    b = valueStack.pop();
                    valueStack.push(Operator.mod(b, a));
                    break;
                default:
                    return false;
            }
        }
        return true;
    }
}
